class BMP280:
    def __init__(self, i2c, addr=0x76):
        self.i2c = i2c
        self.addr = addr
        self.state = 0
        try:
            self._dig_T1 = self._read16(0x88)
        except:
            print("BMP280 init error!\n")
            self.state = -1
            return
        self._dig_T2 = self._readS16(0x8A)
        self._dig_T3 = self._readS16(0x8C)
        self._dig_P1 = self._read16(0x8E)
        self._dig_P2 = self._readS16(0x90)
        self._dig_P3 = self._readS16(0x92)
        self._dig_P4 = self._readS16(0x94)
        self._dig_P5 = self._readS16(0x96)
        self._dig_P6 = self._readS16(0x98)
        self._dig_P7 = self._readS16(0x9A)
        self._dig_P8 = self._readS16(0x9C)
        self._dig_P9 = self._readS16(0x9E)
        self._t_fine = 0
        self._write8(0xF4, 0x2F)  # normal mode, temp and pressure oversampling = 1
        self._write8(0xF5, 0x0C)  # config
    
    def _read8(self, addr):
        return self.i2c.readfrom_mem(self.addr, addr, 1)[0]

    def _write8(self, addr, value):
        self.i2c.writeto_mem(self.addr, addr, bytes([value]))

    def _read16(self, addr):
        data = self.i2c.readfrom_mem(self.addr, addr, 2)
        return data[0] | (data[1] << 8)

    def _readS16(self, addr):
        val = self._read16(addr)
        if val > 32767:
            val -= 65536
        return val

    def read_raw_temp(self):
        msb = self._read8(0xFA)
        lsb = self._read8(0xFB)
        xlsb = self._read8(0xFC)
        return (msb << 12) | (lsb << 4) | (xlsb >> 4)

    def read_raw_press(self):
        msb = self._read8(0xF7)
        lsb = self._read8(0xF8)
        xlsb = self._read8(0xF9)
        return (msb << 12) | (lsb << 4) | (xlsb >> 4)

    @property
    def temperature(self):
        if self.state != 0:
            return -1
        raw_temp = self.read_raw_temp()
        var1 = (raw_temp / 16384.0 - self._dig_T1 / 1024.0) * self._dig_T2
        var2 = ((raw_temp / 131072.0 - self._dig_T1 / 8192.0) * (raw_temp / 131072.0 - self._dig_T1 / 8192.0)) * self._dig_T3
        self._t_fine = int(var1 + var2)
        temp = (var1 + var2) / 5120.0
        return temp

    @property
    def pressure(self):
        if self.state != 0:
            return -1
        raw_press = self.read_raw_press()
        var1 = self._t_fine / 2.0 - 64000.0
        var2 = var1 * var1 * self._dig_P6 / 32768.0
        var2 = var2 + var1 * self._dig_P5 * 2.0
        var2 = var2 / 4.0 + self._dig_P4 * 65536.0
        var1 = (self._dig_P3 * var1 * var1 / 524288.0 + self._dig_P2 * var1) / 524288.0
        var1 = (1.0 + var1 / 32768.0) * self._dig_P1
        if var1 == 0:
            return 0
        p = 1048576.0 - raw_press
        p = (p - var2 / 4096.0) * 6250.0 / var1
        var1 = self._dig_P9 * p * p / 2147483648.0
        var2 = p * self._dig_P8 / 32768.0
        p = p + (var1 + var2 + self._dig_P7) / 16.0
        return p / 100.0

    def get_altitude(self, qnh=1013.25, manual_temperature=None):
        # qnh = pressure at sea level where the readings are being taken.
        # The temperature should be the outdoor temperature.
        # Use the manual_temperature variable if temperature adjustments are required.
        if self.state != 0:
            return -1
        pressure = self.pressure
        if manual_temperature is None:
            temperature = self.temperature
        else:
            temperature = manual_temperature
        altitude = ((pow((qnh / pressure), (1.0 / 5.257)) - 1) * (temperature + 273.15)) / 0.0065
        return altitude